package app

import (
	"encoding/json"
	"net/http"
	"strconv"
	"time"
)

type Result struct {
	Success   bool   `json:"success"`
	Timestamp int64  `json:"timestamp"`
	Code      int    `json:"code,omitempty"`
	Msg       string `json:"msg,omitempty"`

	Data  interface{} `json:"data,omitempty"`
	Total int         `json:"total,omitempty"`
}

func NewResult() *Result {
	return &Result{
		Code:      -1,
		Timestamp: time.Now().UnixMilli(),
	}
}

type Creator interface {
	Create() interface{}
}

type CreatorFn func() interface{}
type MsgHandler func(interface{}) (interface{}, error)
type HttpMsgHandler func(http.ResponseWriter, *http.Request, interface{}) (interface{}, error)

type JsonMsgConvert struct {
	Creator CreatorFn
	Handler HttpMsgHandler
}

func (msg MsgHandler) handle(_ http.ResponseWriter, _ *http.Request, req interface{}) (interface{}, error) {
	return msg(req)
}

func Json(creator CreatorFn, handler MsgHandler) http.HandlerFunc {
	cvr := &JsonMsgConvert{
		Creator: creator,
		Handler: handler.handle,
	}
	return cvr.ServeHTTP
}

func JsonHttp(creator CreatorFn, handler HttpMsgHandler) http.HandlerFunc {
	cvr := &JsonMsgConvert{
		Creator: creator,
		Handler: handler,
	}
	return cvr.ServeHTTP
}

func (c *JsonMsgConvert) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var err error
	var obj interface{}
	if c.Creator != nil {
		obj = c.Creator()
		err = json.NewDecoder(r.Body).Decode(obj)
	}
	var rs interface{}
	if err == nil {
		rs, err = c.Handler(w, r, obj)
	}
	RenderJSON(w, rs, err)
}
func ReadJSON(r *http.Request, t interface{}) error {
	return json.NewDecoder(r.Body).Decode(t)
}
func RenderJSON(w http.ResponseWriter, rs interface{}, err error) {
	w.Header().Set("Content-Type", "application/json")
	result := &Result{
		Code:      0,
		Timestamp: time.Now().UnixMilli(),
		Data:      rs,
	}

	if err != nil {
		result.Msg = err.Error()
		result.Code = -1
	} else {
		result.Success = true
	}
	_ = json.NewEncoder(w).Encode(result)
}

func StringToInt(s string) (int, error) {
	return strconv.Atoi(s)
}
func StringToFloat32(s string) (float32, error) {
	v, err := strconv.ParseFloat(s, 64)
	if err == nil {
		return float32(v), err
	}
	return 0, err
}
